package com.datagateway.component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.*;

/**
 * 数据压缩管理器
 * 支持多种压缩算法，减少网络传输和存储开销
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@Component
public class DataCompressionManager {

    private static final Logger logger = LoggerFactory.getLogger(DataCompressionManager.class);

    /**
     * 压缩算法枚举
     */
    public enum CompressionAlgorithm {
        GZIP("gzip"),
        DEFLATE("deflate"),
        LZ4("lz4"),
        SNAPPY("snappy");

        private final String name;

        CompressionAlgorithm(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    /**
     * 压缩统计信息
     */
    private final AtomicLong totalCompressedBytes = new AtomicLong(0);
    private final AtomicLong totalOriginalBytes = new AtomicLong(0);
    private final AtomicLong totalCompressionCount = new AtomicLong(0);
    private final AtomicLong totalDecompressionCount = new AtomicLong(0);

    /**
     * 默认压缩算法
     */
    private CompressionAlgorithm defaultAlgorithm = CompressionAlgorithm.GZIP;

    /**
     * 压缩数据
     * 
     * @param data 原始数据
     * @param algorithm 压缩算法
     * @return 压缩后的数据
     */
    public byte[] compress(byte[] data, CompressionAlgorithm algorithm) {
        if (data == null || data.length == 0) {
            return data;
        }

        long startTime = System.currentTimeMillis();
        
        try {
            byte[] compressedData = doCompress(data, algorithm);
            
            long processingTime = System.currentTimeMillis() - startTime;
            long originalSize = data.length;
            long compressedSize = compressedData.length;
            double compressionRatio = (double) compressedSize / originalSize;
            
            // 更新统计信息
            totalCompressedBytes.addAndGet(compressedSize);
            totalOriginalBytes.addAndGet(originalSize);
            totalCompressionCount.incrementAndGet();
            
            logger.debug("数据压缩完成: 算法={}, 原始大小={} bytes, 压缩后大小={} bytes, " +
                        "压缩比={:.2f}, 耗时={} ms", 
                        algorithm.getName(), originalSize, compressedSize, compressionRatio, processingTime);
            
            return compressedData;
            
        } catch (Exception e) {
            logger.error("数据压缩失败: 算法={}, 数据大小={} bytes", algorithm.getName(), data.length, e);
            throw new RuntimeException("数据压缩失败", e);
        }
    }

    /**
     * 压缩数据（使用默认算法）
     * 
     * @param data 原始数据
     * @return 压缩后的数据
     */
    public byte[] compress(byte[] data) {
        return compress(data, defaultAlgorithm);
    }

    /**
     * 解压数据
     * 
     * @param compressedData 压缩后的数据
     * @param algorithm 压缩算法
     * @return 解压后的数据
     */
    public byte[] decompress(byte[] compressedData, CompressionAlgorithm algorithm) {
        if (compressedData == null || compressedData.length == 0) {
            return compressedData;
        }

        long startTime = System.currentTimeMillis();
        
        try {
            byte[] decompressedData = doDecompress(compressedData, algorithm);
            
            long processingTime = System.currentTimeMillis() - startTime;
            long compressedSize = compressedData.length;
            long decompressedSize = decompressedData.length;
            
            // 更新统计信息
            totalDecompressionCount.incrementAndGet();
            
            logger.debug("数据解压完成: 算法={}, 压缩大小={} bytes, 解压后大小={} bytes, 耗时={} ms", 
                        algorithm.getName(), compressedSize, decompressedSize, processingTime);
            
            return decompressedData;
            
        } catch (Exception e) {
            logger.error("数据解压失败: 算法={}, 数据大小={} bytes", algorithm.getName(), compressedData.length, e);
            throw new RuntimeException("数据解压失败", e);
        }
    }

    /**
     * 解压数据（使用默认算法）
     * 
     * @param compressedData 压缩后的数据
     * @return 解压后的数据
     */
    public byte[] decompress(byte[] compressedData) {
        return decompress(compressedData, defaultAlgorithm);
    }

    /**
     * 执行压缩
     * 
     * @param data 原始数据
     * @param algorithm 压缩算法
     * @return 压缩后的数据
     */
    private byte[] doCompress(byte[] data, CompressionAlgorithm algorithm) throws IOException {
        switch (algorithm) {
            case GZIP:
                return compressWithGzip(data);
            case DEFLATE:
                return compressWithDeflate(data);
            case LZ4:
                return compressWithLZ4(data);
            case SNAPPY:
                return compressWithSnappy(data);
            default:
                throw new IllegalArgumentException("不支持的压缩算法: " + algorithm);
        }
    }

    /**
     * 执行解压
     * 
     * @param compressedData 压缩后的数据
     * @param algorithm 压缩算法
     * @return 解压后的数据
     */
    private byte[] doDecompress(byte[] compressedData, CompressionAlgorithm algorithm) throws IOException {
        switch (algorithm) {
            case GZIP:
                return decompressWithGzip(compressedData);
            case DEFLATE:
                return decompressWithDeflate(compressedData);
            case LZ4:
                return decompressWithLZ4(compressedData);
            case SNAPPY:
                return decompressWithSnappy(compressedData);
            default:
                throw new IllegalArgumentException("不支持的压缩算法: " + algorithm);
        }
    }

    /**
     * 使用GZIP压缩
     */
    private byte[] compressWithGzip(byte[] data) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             GZIPOutputStream gzipOut = new GZIPOutputStream(baos)) {
            
            gzipOut.write(data);
            gzipOut.finish();
            return baos.toByteArray();
        }
    }

    /**
     * 使用GZIP解压
     */
    private byte[] decompressWithGzip(byte[] compressedData) throws IOException {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(compressedData);
             GZIPInputStream gzipIn = new GZIPInputStream(bais);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = gzipIn.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        }
    }

    /**
     * 使用DEFLATE压缩
     */
    private byte[] compressWithDeflate(byte[] data) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             DeflaterOutputStream deflateOut = new DeflaterOutputStream(baos)) {
            
            deflateOut.write(data);
            deflateOut.finish();
            return baos.toByteArray();
        }
    }

    /**
     * 使用DEFLATE解压
     */
    private byte[] decompressWithDeflate(byte[] compressedData) throws IOException {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(compressedData);
             InflaterInputStream inflateIn = new InflaterInputStream(bais);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inflateIn.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        }
    }

    /**
     * 使用LZ4压缩（简化实现，实际项目中应使用LZ4库）
     */
    private byte[] compressWithLZ4(byte[] data) throws IOException {
        // 这里使用DEFLATE作为LZ4的替代实现
        // 实际项目中应该使用真正的LZ4库，如lz4-java
        logger.warn("LZ4压缩使用DEFLATE替代实现，建议使用真正的LZ4库");
        return compressWithDeflate(data);
    }

    /**
     * 使用LZ4解压（简化实现，实际项目中应使用LZ4库）
     */
    private byte[] decompressWithLZ4(byte[] compressedData) throws IOException {
        // 这里使用DEFLATE作为LZ4的替代实现
        // 实际项目中应该使用真正的LZ4库，如lz4-java
        logger.warn("LZ4解压使用DEFLATE替代实现，建议使用真正的LZ4库");
        return decompressWithDeflate(compressedData);
    }

    /**
     * 使用Snappy压缩（简化实现，实际项目中应使用Snappy库）
     */
    private byte[] compressWithSnappy(byte[] data) throws IOException {
        // 这里使用DEFLATE作为Snappy的替代实现
        // 实际项目中应该使用真正的Snappy库，如snappy-java
        logger.warn("Snappy压缩使用DEFLATE替代实现，建议使用真正的Snappy库");
        return compressWithDeflate(data);
    }

    /**
     * 使用Snappy解压（简化实现，实际项目中应使用Snappy库）
     */
    private byte[] decompressWithSnappy(byte[] compressedData) throws IOException {
        // 这里使用DEFLATE作为Snappy的替代实现
        // 实际项目中应该使用真正的Snappy库，如snappy-java
        logger.warn("Snappy解压使用DEFLATE替代实现，建议使用真正的Snappy库");
        return decompressWithDeflate(compressedData);
    }

    /**
     * 设置默认压缩算法
     * 
     * @param algorithm 压缩算法
     */
    public void setDefaultAlgorithm(CompressionAlgorithm algorithm) {
        this.defaultAlgorithm = algorithm;
        logger.info("默认压缩算法已更新: {}", algorithm.getName());
    }

    /**
     * 获取默认压缩算法
     * 
     * @return 压缩算法
     */
    public CompressionAlgorithm getDefaultAlgorithm() {
        return defaultAlgorithm;
    }

    /**
     * 获取压缩统计信息
     * 
     * @return 统计信息
     */
    public CompressionStatistics getStatistics() {
        long totalCompressed = totalCompressedBytes.get();
        long totalOriginal = totalOriginalBytes.get();
        double averageCompressionRatio = totalOriginal > 0 ? (double) totalCompressed / totalOriginal : 0;
        
        return new CompressionStatistics(
            totalCompressionCount.get(),
            totalDecompressionCount.get(),
            totalOriginal,
            totalCompressed,
            averageCompressionRatio,
            defaultAlgorithm,
            System.currentTimeMillis()
        );
    }

    /**
     * 重置统计信息
     */
    public void resetStatistics() {
        totalCompressedBytes.set(0);
        totalOriginalBytes.set(0);
        totalCompressionCount.set(0);
        totalDecompressionCount.set(0);
        logger.info("压缩统计信息已重置");
    }

    /**
     * 压缩统计信息类
     */
    public static class CompressionStatistics {
        private final long totalCompressionCount;
        private final long totalDecompressionCount;
        private final long totalOriginalBytes;
        private final long totalCompressedBytes;
        private final double averageCompressionRatio;
        private final CompressionAlgorithm defaultAlgorithm;
        private final long timestamp;

        public CompressionStatistics(long totalCompressionCount, long totalDecompressionCount,
                                   long totalOriginalBytes, long totalCompressedBytes,
                                   double averageCompressionRatio, CompressionAlgorithm defaultAlgorithm,
                                   long timestamp) {
            this.totalCompressionCount = totalCompressionCount;
            this.totalDecompressionCount = totalDecompressionCount;
            this.totalOriginalBytes = totalOriginalBytes;
            this.totalCompressedBytes = totalCompressedBytes;
            this.averageCompressionRatio = averageCompressionRatio;
            this.defaultAlgorithm = defaultAlgorithm;
            this.timestamp = timestamp;
        }

        // Getter方法
        public long getTotalCompressionCount() { return totalCompressionCount; }
        public long getTotalDecompressionCount() { return totalDecompressionCount; }
        public long getTotalOriginalBytes() { return totalOriginalBytes; }
        public long getTotalCompressedBytes() { return totalCompressedBytes; }
        public double getAverageCompressionRatio() { return averageCompressionRatio; }
        public CompressionAlgorithm getDefaultAlgorithm() { return defaultAlgorithm; }
        public long getTimestamp() { return timestamp; }
        
        public long getTotalBytesSaved() { return totalOriginalBytes - totalCompressedBytes; }
        public double getCompressionEfficiency() { return 1.0 - averageCompressionRatio; }
    }
}
